/**
 * Copyright (C) 2025 pony working team
 * SPDX-License-Identifier: MulanPSL-2.0
 */

/* includes (local) ----------------------------------------------------------*/
#include <pony/sensor/stts22h.h>
#include <hal_i2c.h>
/* includes (standard library, system) ---------------------------------------*/
/* includes (other library) --------------------------------------------------*/
/* includes (project) --------------------------------------------------------*/
#include <x_config.h>
#include <x_module.h>

#include <hal_delay.h>

/* defines -------------------------------------------------------------------*/

#define PROPERTY_DISABLE (0U)
#define PROPERTY_ENABLE  (1U)

/* typedefs ------------------------------------------------------------------*/

typedef struct stts22h {
    hal_i2c_dev_t dev;
    uint8_t is_initialized;
    uint8_t temp_is_enabled;
} stts22h_t;

/* macro ---------------------------------------------------------------------*/
/* variables (extern) --------------------------------------------------------*/
/* variables (local) ---------------------------------------------------------*/

static stts22h_t s_stts22h;

/* variables (global) --------------------------------------------------------*/
/* inlines -------------------------------------------------------------------*/
/* functions -----------------------------------------------------------------*/

static int __stts22h_write_reg(hal_i2c_dev_t *dev, uint8_t reg, uint8_t *data, uint16_t len)
{
    DELAY_MS(2);
    return hal_i2c_dev_write(dev, reg, data, len);
}

static int __stts22h_read_reg(hal_i2c_dev_t *dev, uint8_t reg, uint8_t *data, uint16_t len)
{
    DELAY_MS(2);
    return hal_i2c_dev_read(dev, reg, data, len);
}

static int __stts22h_block_data_update_set(hal_i2c_dev_t *dev, uint8_t val)
{
    stts22h_ctrl_t ctrl;
    int ret;

    ret = __stts22h_read_reg(dev, STTS22H_REG_CTRL, &ctrl.byte, 1);

    if (ret == 0) {
        ctrl.bits.bdu = val;
        ret = __stts22h_write_reg(dev, STTS22H_REG_CTRL, &ctrl.byte, 1);
    }

    return ret;
}

__unused static inline int __stts22h_block_data_update_get(hal_i2c_dev_t *dev, uint8_t *val)
{
    return __stts22h_read_reg(dev, STTS22H_REG_CTRL, val, 1);
}

__unused static int __stts22h_temp_flag_data_ready_get(hal_i2c_dev_t *dev, uint8_t *val)
{
    stts22h_status_t status;
    int ret;

    ret = __stts22h_read_reg(dev, STTS22H_REG_STATUS, &status.byte, 1);

    if (status.bits.busy == PROPERTY_DISABLE) {
        *val = PROPERTY_ENABLE;
    } else {
        *val = PROPERTY_DISABLE;
    }

    return ret;
}

static int __stts22h_temperature_raw_get(hal_i2c_dev_t *dev, int16_t *val)
{
    uint8_t buf[2];
    int ret;

    ret = __stts22h_read_reg(dev, STTS22H_REG_TEMP_L_OUT, buf, 2);
    *val = (int16_t)buf[1];
    *val = (*val * 256) + (int16_t)buf[0];

    return ret;
}

/**
 * Register address automatically incremented during a multiple
 * byte access with a serial interface
 */
__unused static int __stts22h_auto_increment_set(hal_i2c_dev_t *dev, uint8_t val)
{
    stts22h_ctrl_t ctrl;
    int ret;

    ret = __stts22h_read_reg(dev, STTS22H_REG_CTRL, &ctrl.byte, 1);

    if (ret == 0) {
        ctrl.bits.if_add_inc = (uint8_t)val;
        ret = __stts22h_write_reg(dev, STTS22H_REG_CTRL, &ctrl.byte, 1);
    }

    return ret;
}

static int __stts22h_temp_data_rate_set(hal_i2c_dev_t *dev, stts22h_odr_temp_t val)
{
    stts22h_software_reset_t sr;
    stts22h_ctrl_t ctrl;
    int ret;

    ret = __stts22h_read_reg(dev, STTS22H_REG_CTRL, &ctrl.byte, 1);

    if (ret == 0) {
        ret = __stts22h_read_reg(dev, STTS22H_REG_SOFTWARE_RESET, &sr.byte, 1);
    }

    if ((val == STTS22H_ONE_SHOT) && (ret == 0)) {
        sr.bits.sw_reset = PROPERTY_ENABLE;
        ret = __stts22h_write_reg(dev, STTS22H_REG_SOFTWARE_RESET, &sr.byte, 1);

        if (ret == 0) {
            sr.bits.sw_reset = PROPERTY_DISABLE;
            ret = __stts22h_write_reg(dev, STTS22H_REG_SOFTWARE_RESET, &sr.byte, 1);
        }
    }

    if (((val == STTS22H_25Hz) || (val == STTS22H_50Hz) || (val == STTS22H_100Hz) ||
         (val == STTS22H_200Hz)) &&
        (ctrl.bits.freerun == PROPERTY_DISABLE) && (ret == 0)) {
        sr.bits.sw_reset = PROPERTY_ENABLE;
        ret = __stts22h_write_reg(dev, STTS22H_REG_SOFTWARE_RESET, &sr.byte, 1);

        if (ret == 0) {
            sr.bits.sw_reset = PROPERTY_DISABLE;
            ret = __stts22h_write_reg(dev, STTS22H_REG_SOFTWARE_RESET, &sr.byte, 1);
        }
    }

    if ((val == STTS22H_1Hz) && (ret == 0)) {
        sr.bits.sw_reset = PROPERTY_ENABLE;
        sr.bits.low_odr_enable = PROPERTY_ENABLE;
        ret = __stts22h_write_reg(dev, STTS22H_REG_SOFTWARE_RESET, &sr.byte, 1);

        if (ret == 0) {
            sr.bits.sw_reset = PROPERTY_DISABLE;
            sr.bits.low_odr_enable = PROPERTY_ENABLE;
            ret = __stts22h_write_reg(dev, STTS22H_REG_SOFTWARE_RESET, &sr.byte, 1);
        }
    }

    if (ret == 0) {
        ctrl.bits.one_shot = (uint8_t)val & 0x01U;
        ctrl.bits.freerun = ((uint8_t)val & 0x02U) >> 1;
        ctrl.bits.low_odr_start = ((uint8_t)val & 0x04U) >> 2;
        ctrl.bits.avg = ((uint8_t)val & 0x30U) >> 4;
        ret = __stts22h_write_reg(dev, STTS22H_REG_CTRL, &ctrl.byte, 1);
    }

    return ret;
}

static int __stts22h_init(hal_i2c_dev_t *dev)
{
    stts22h_t *temp_dev = container_of(dev, stts22h_t, dev);

    if (temp_dev->is_initialized == 0) {
        /* Enable BDU */
        if (__stts22h_block_data_update_set(dev, PROPERTY_ENABLE) != 0) {
            return -1;
        }
        /* Enable Automatic Address Increment */
        if (__stts22h_auto_increment_set(dev, PROPERTY_ENABLE) != 0) {
            return -2;
        }
        /* Put the component in standby mode. */
        if (__stts22h_temp_data_rate_set(dev, STTS22H_POWER_DOWN) != 0) {
            return -3;
        }
    }
    temp_dev->is_initialized = 1;

    if (temp_dev->temp_is_enabled == 0) {
        /* Power on the component and set the odr. */
        if (__stts22h_temp_data_rate_set(dev, STTS22H_1Hz) != 0) {
            return -4;
        }
    }
    temp_dev->temp_is_enabled = 1;

    return 0;
}

static int __stts22h_open(hal_dev_t *dev, int oflag, va_list valist)
{
    hal_i2c_dev_t *i2c_dev = (hal_i2c_dev_t *)dev;

    return __stts22h_init(i2c_dev);
}

static hal_dev_ops_t dev_ops = {
    .open = __stts22h_open,
};

static int stts22h_init(void)
{
    s_stts22h.dev.masize = 1;
    if (hal_i2c_dev_register(&s_stts22h.dev, CONFIG_STTS22H_I2C_BUS, "stts22h",
                             CONFIG_STTS22H_I2C_ADDR) != 0) {
        return -1;
    }
    if (hal_dev_init((hal_dev_t *)&s_stts22h, &dev_ops) != 0) {
        return -2;
    }
    return 0;
}

MODULE_INIT(driver, stts22h_init);

uint8_t stts22h_read_id(hal_i2c_dev_t *dev)
{
    uint8_t id = 0;
    __stts22h_read_reg(dev, STTS22H_REG_WHOAMI, &id, 1);
    return id;
}

int stts22h_read_temp(hal_i2c_dev_t *dev, int16_t *temp)
{
    if (temp == NULL) {
        return -1;
    }
    /* Get the temperature */
    if (__stts22h_temperature_raw_get(dev, temp) != 0) {
        return -2;
    }

    return 0;
}
